#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h> //client (gethostbyname())
#include <stdlib.h>
#include <stdio.h> //errno, printf ..
#include <sys/un.h>//bzero
#include "communicatorUtil.hh"

//time out
//#include <ctype.h>
//#include <sys/time.h>
#include <fcntl.h>
#include <time.h>
#include <iostream> 

using namespace std;
e_status runServerAndConnect(const char* name, int port, int* serverSocket, int* clientSocket){
    struct sockaddr_in server_addr,client_addr;

    int sin_size;
    if ((*serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        return ERROR;
    }
    int op_val=1;
    if (setsockopt(*serverSocket,SOL_SOCKET,SO_REUSEADDR,&op_val,sizeof(int)) == -1) {
        perror("Setsockopt");
        return ERROR;

    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero),8);

    if (bind(*serverSocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))
                                                                   == -1) {
        perror("Unable to bind");
        return ERROR;
    }

    if (listen(*serverSocket, 1) == -1) {
        perror("Listen");
        return ERROR;
    }

    printf("TCPServer Waiting for %s on port %d\n",name,  port);
    fflush(stdout);
    sin_size = sizeof(struct sockaddr_in);

    *clientSocket = accept(*serverSocket, (struct sockaddr *)&client_addr,(socklen_t*)&sin_size);

    printf("got a connection from %s : (%s , %d)\n", name,
           inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
    fflush(stdout);


}

e_status connectToClient(const char* name,const char* addr, int port, int* sock){
    struct hostent* host;
    struct sockaddr_in serverAddr;

    host = gethostbyname(addr);

    if ((*sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        return ERROR;
    }

    serverAddr.sin_family = AF_INET;

    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(serverAddr.sin_zero),8);

    if (connect(*sock, (struct sockaddr *)&serverAddr,
                sizeof(struct sockaddr)) == -1)
    {
        printf("cannot connect to %s\n", name);
        perror("connection");
        return ERROR;
    }

    return SUCCESS;

}
e_status terminateSocket(int sock){

    if(shutdown(sock,2) == -1)
    {   
        perror("shutdown");
        return ERROR;
    }
    if(close(sock) == -1){
     
        perror("close");
        return ERROR;
    }
    return SUCCESS;

}

/*
   Params:
      fd       -  (int) socket file descriptor
      buffer - (char*) buffer to hold data
      len     - (int) maximum number of bytes to recv()
      flags   - (int) flags (as the fourth param to recv() )
      to       - (int) timeout in milliseconds
   Results:
      int      - The same as recv, but -2 == TIMEOUT
   Notes:
      You can only use it on file descriptors that are sockets!
      'to' must be different to 0
      'buffer' must not be NULL and must point to enough memory to hold at least 'len' bytes
      I WILL mix the C and C++ commenting styles...
*/
int recv_to(int fd, char *buffer, int len, int flags, int to) {

   int result=0, iof = -1;
   time_t t1, t2;
   time(&t1);
   time(&t2);
   // Set non-blocking mode
   if ((iof = fcntl(fd, F_GETFL, 0)) != -1)
      fcntl(fd, F_SETFL, iof | O_NONBLOCK);
   // receive
   while (result < 1){
   
       result = recv(fd, buffer, len, flags);
       time(&t2);
       if(t2-t1 >= to){
           result = -2; 
           break;
       }
   }
   // set as before
   if (iof != -1)
      fcntl(fd, F_SETFL, iof);
   
   return result;
}

e_status readChar(int sock, char t){
     // Set non-blocking mode

    char recv_data = 0;//one byte data is required
    int iof = -1;

    if ((iof = fcntl(sock, F_GETFL, 0)) != -1)
        fcntl(sock, F_SETFL, iof | O_NONBLOCK);
      // receive
      int result = recv(sock, &recv_data, 1, 0);

      if(result == 0)
          return DISCONNECTED;
      else if(result == -1)
          return  ERROR;

      // set as before
      if (iof != -1)
         fcntl(sock, F_SETFL, iof);

      if(recv_data == t){
          return  SUCCESS;
      }

      return ERROR;


}

